package com.jinyang.jetpackdemo.activity

import android.content.Context
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
import androidx.datastore.core.DataStore
import androidx.datastore.core.Serializer
import androidx.datastore.dataStore
import androidx.datastore.migrations.SharedPreferencesMigration
import androidx.lifecycle.asLiveData
import androidx.lifecycle.lifecycleScope
import com.jinyang.jetpackdemo.R
import com.jinyang.jetpackdemo.datastore.User
import com.jinyang.jetpackdemo.util.LjyLogUtil
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.io.InputStream
import java.io.OutputStream
import com.tencent.mmkv.MMKV


//1. 创建 Preferences DataStore
// 在Kotlin 文件顶层调用该实例一次，便可在应用的所有其余部分通过此属性访问该实例。这样可以更轻松地将 DataStore 保留为单例
//val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = Constants.MY_PREFERENCES)
//val Context.dataStore2 by preferencesDataStore(
//    name = Constants.SP_2_PREFERENCES,
//    produceMigrations = { context ->
//        // Since we're migrating from SharedPreferences, add a migration based on the
//        // SharedPreferences name
//        listOf(SharedPreferencesMigration(context, Constants.MY_SP))
//    }
//)

val Context.userInfoStore: DataStore<User> by dataStore(
    fileName = "userInfo.pb",
    serializer = UserSerializer
)
val Context.userInfoStore2: DataStore<User> by dataStore(
    fileName = "userInfo22.pb",
    serializer = UserSerializer,
    produceMigrations = { context ->
        listOf(SharedPreferencesMigration(context, Constants.MY_SP) { sharedPrefs, user ->
            //将sp的键值对映射到User
            user.toBuilder()
                .setName(sharedPrefs.getString(Constants.KEY_NAME_SP))
                .setAge(sharedPrefs.getInt(Constants.KEY_AGE_SP, 0))
                .setIsMarried(false)
                .build()
        })
    }
)


object Constants {
    //SharedPreferences文件名
    const val MY_SP = "mySP2"

    //DataStore<Preferences>文件名
    const val MY_PREFERENCES = "myPreferences"

    //SharedPreferences 迁移到 DataStore<Preferences> 后的文件名
    const val SP_2_PREFERENCES = "sp2Preferences"

    //SharedPreferences中的key
    const val KEY_NAME_SP = "name"
    const val KEY_AGE_SP = "age"

    //SharedPreferences 迁移到 DataStore<Preferences> 后的key
//    val KEY_NAME = stringPreferencesKey(KEY_NAME_SP)
//
//    //DataStore<Preferences> 中的key
//    val KEY_USER_NAME = stringPreferencesKey("userName")
//    val KEY_USER_AGE = intPreferencesKey("userAge")
}

class DataStoreActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_data_store)
    }


    fun onBtnCLick(view: View) {
        when (view.id) {
            R.id.btn_add_sp -> {
                setNameSP()
            }
            R.id.btn_dataStorePreferences -> {
                lifecycleScope.launch {
                    dataStorePreferences()
                }
            }
            R.id.btn_sp2ds -> {
                lifecycleScope.launch {
                    sp2DataStore()
                }
            }

            R.id.btn_clear -> {
                lifecycleScope.launch {
                    clear()
                }
            }
            R.id.btn_proto -> {
                lifecycleScope.launch {
                    dataStoreProto()
                }
            }
            R.id.btn_sp2proto -> {
                lifecycleScope.launch {
                    sp2Proto()
                }
            }

            R.id.btn_mmkv -> {
                mmkvDemo()
            }
        }
    }

    private fun mmkvDemo() {
        //获取单例
        val kv = MMKV.defaultMMKV()
        //写入数据
        kv.encode("name", "LJY")
        kv.encode("age", 16)
        kv.encode("isMarried", true)
        //读取数据
        val name = kv.decodeString("name")
        LjyLogUtil.d("name=$name")
        val age = kv.decodeInt("age")
        LjyLogUtil.d("age=$age")
        val isMarried = kv.decodeBool("isMarried")
        LjyLogUtil.d("isMarried=$isMarried")
        //删除数据
        kv.removeValueForKey("age");
        LjyLogUtil.d("age=${kv.decodeInt("age")}")
        //如果不同业务需要区别存储，也可以单独创建自己的实例
        val kvUser = MMKV.mmkvWithID("userInfo")
        kvUser.encode("name", "yang")
        LjyLogUtil.d("name=${kvUser.decodeString("name")}")
        //如果业务需要多进程访问，那么在初始化的时候加上标志位 MMKV.MULTI_PROCESS_MODE
        val kvSetting = MMKV.mmkvWithID("settings", MMKV.MULTI_PROCESS_MODE)
        kvSetting.encode("key", "abc")
        LjyLogUtil.d("key=${kvSetting.decodeString("key")}")
        //SharedPreferences 迁移
//        val preferences = getSharedPreferences("myData", MODE_PRIVATE)
        val sp2mmkv: MMKV = MMKV.mmkvWithID("myData")
        // 迁移旧数据:
        //MMKV 还额外实现了一遍 SharedPreferences、SharedPreferences.Editor 这两个 interface，
        // 在迁移的时候只需两三行代码即可，其他 CRUD 操作代码都不用改
        val sp = getSharedPreferences(Constants.MY_SP, MODE_PRIVATE)
        sp2mmkv.importFromSharedPreferences(sp)
        sp.edit().clear().apply()
        // 跟以前用法一样
        sp2mmkv.edit(commit = true){
            putBoolean("bool", true)
            val set = HashSet<String>()
            set.add("a")
            set.add("b")
            set.add("c")
            putStringSet("string-set", set)
        }
        LjyLogUtil.d("name=${sp2mmkv.getString(Constants.KEY_NAME_SP,"")}")
        LjyLogUtil.d("age=${sp2mmkv.getInt(Constants.KEY_AGE_SP,0)}")
        LjyLogUtil.d("bool=${sp2mmkv.getBoolean("bool",false)}")
        LjyLogUtil.d("string-set=${sp2mmkv.getStringSet("string-set", emptySet())}")
    }

    private fun sp2Proto() {
        userInfoStore2.data.asLiveData().observe(this) {
            LjyLogUtil.d("asLiveData：it:$it")
            LjyLogUtil.d("name:${it.name}")
            LjyLogUtil.d("age:${it.age}")
            LjyLogUtil.d("isMarried:${it.isMarried}")
        }
        lifecycleScope.launch {
            val user = userInfoStore2.data.first()
            LjyLogUtil.d("user:$user")
        }
    }

    private fun dataStoreProto() {
        // 注册观察者读取内容
        userInfoStore.data.asLiveData().observe(this) {
            LjyLogUtil.d("asLiveData：it:$it")
            LjyLogUtil.d("name:${it.name}")
            LjyLogUtil.d("age:${it.age}")
            LjyLogUtil.d("isMarried:${it.isMarried}")
        }
        lifecycleScope.launch {
            //将内容写入 Proto DataStore
            userInfoStore.updateData {
                it.toBuilder()
                    .setName("今阳")
                    .setAge(18)
                    .setIsMarried(true)
                    .build()
            }
            //使用collect读取内容
            userInfoStore.data.collect {
                LjyLogUtil.d("collect: it:$it")
            }
        }
        // DataStore 的主要优势之一是异步 API，但可能不一定始终能将周围的代码更改为异步代码。
        // 如果您使用的现有代码库采用同步磁盘 I/O，或者您的依赖项不提供异步 API，就可能出现这种情况。
        // Kotlin 协程提供 runBlocking() 协程构建器，以帮助消除同步与异步代码之间的差异。
        // 您可以使用 runBlocking() 从 DataStore 同步读取数据。RxJava 在 Flowable 上提供阻塞方法。
        // 以下代码会阻塞发起调用的线程，直到 DataStore 返回数据：
        val user = runBlocking { userInfoStore.data.first() }
        LjyLogUtil.d("runBlocking: user:$user")
        //对界面线程执行同步 I/O 操作可能会导致 ANR 或界面卡顿。您可以通过从 DataStore 异步预加载数据来减少这些问题：
        lifecycleScope.launch {
            val user = userInfoStore.data.first()
            LjyLogUtil.d("user:$user")
        }
    }

    suspend fun clear() {
//        //4. 清除数据
//        dataStore2.edit {
//            it.clear()
//        }
    }

    private suspend fun sp2DataStore() {
//        //5. 迁移 SharedPreferences 到 DataStore
//        dataStore2.data.collect {
//            LjyLogUtil.d("it:$it")
//        }
    }

    private suspend fun dataStorePreferences() {
//        //2. 存储键值对数据
//        dataStore.edit {
//            it[Constants.KEY_USER_NAME] = "jinYang"
//            it[Constants.KEY_USER_AGE] = 18
//        }
//        //3. 读取键值对数据
//        val flow = dataStore.data
//        flow.collect {
//            LjyLogUtil.d("collect：userName:${it[Constants.KEY_USER_NAME]}")
//            LjyLogUtil.d("collect：userAge:${it[Constants.KEY_USER_AGE]}")
//            LjyLogUtil.d("collect：it:$it")
//        }
//        //或者
//        dataStore.data.asLiveData().observe(this) {
//            LjyLogUtil.d("asLiveData：userName:${it[Constants.KEY_USER_NAME]}")
//            LjyLogUtil.d("asLiveData：userAge:${it[Constants.KEY_USER_AGE]}")
//            LjyLogUtil.d("asLiveData：it:$it")
//        }

    }

    private fun setNameSP() {
        val sp = getSharedPreferences(Constants.MY_SP, MODE_PRIVATE)
        //sp.edit默认是apply，可以手动设置为commit
        //apply:提交会先写入内存，然后异步写入磁盘
        //commit:直接写入磁盘
        //如果频繁操作的话 apply 的性能会优于 commit
        //提交数据时， 尽量使用apply,而非commit，仅当需要确定提交结果，并据此有后续操作时，使用commit;
        sp.edit(commit = true) {
            putString(Constants.KEY_NAME_SP, "洋仔")
            putInt(Constants.KEY_AGE_SP, 17)
        }
        val valueNew = sp.getString(Constants.KEY_NAME_SP, "")
        //        val value=sp.getBoolean(key,false)
        LjyLogUtil.d("sp: ${Constants.KEY_NAME_SP}=$valueNew")
    }
}

object UserSerializer : Serializer<User> {
    override val defaultValue: User
        get() = User.getDefaultInstance()

    override suspend fun readFrom(input: InputStream): User {
        return User.parseFrom(input)
    }

    override suspend fun writeTo(t: User, output: OutputStream) {
        t.writeTo(output)
    }
}

